﻿using SCG.General;
using SCG.SolarVengeanceEngine;
using SCG.SolarVengeanceEngine.BrainInfo;
using System;
using System.Collections.Generic;

namespace SCG.SolarVengeance.Brains
{
    public class Athenus : Brain
    {
        //challenge level
        public override ChallengeLevel Challenge
        {
            get
            {
                return ChallengeLevel.Difficult;
            }
        }

        //ship types
        public override List<StarShipType> AvailableShipTypes
        {
            get
            {
                List<StarShipType> available = new List<StarShipType>();
                available.Add(StarShipType.WarShip);
                available.Add(StarShipType.EMine);
                available.Add(StarShipType.TechShip);
                available.Add(StarShipType.InkSpot);
                available.Add(StarShipType.StarTillery);
                available.Add(StarShipType.Drone);
                return available;
            }
        }

        //execute brain code
        public override void Execute()
        {
            //initialize
            foreach(StarSystemInfo sys in StarSystems)
            {
                if (sys.BrainData == null)
                    sys.BrainData = new StarSystemData();
                StarSystemData sd = (StarSystemData)sys.BrainData;
                if (!sys.Visible)
                    sd.ShieldsRemaining = 6;
                else
                    sd.ShieldsRemaining = sys.Shields;
                sd.ClosestEnemy = 9999;
                sd.Defenders = 0;
            }
            
            foreach(StarShipInfo ship in StarShips)
            {
                if (ship.BrainData == null)
                    ship.BrainData = new StarShipData();
                StarShipData sd = (StarShipData)ship.BrainData;
                sd.Targeted = false;
            }
            foreach(StarShipInfo ship in StarShips)
            {
                StarShipData sd = (StarShipData)ship.BrainData;
                if (sd.TargetSystemA != null)
                {
                    StarSystemData sysData = (StarSystemData)sd.TargetSystemA.BrainData;
                    sysData.ShieldsRemaining -= ship.Value;
                }
                if (sd.TargetCode != null)
                {
                    StarShipInfo targ = FindStarShip(sd.TargetCode);
                    if (targ != null)
                    {
                        StarShipData targData = (StarShipData)targ.BrainData;
                        targData.Targeted = true;
                    }
                }
            }

            //collect intelligence
            int mySystems = 0;
            int bestWeaponsSeen = 0;
            int numTS = 0;
            int numST = 0;
            int numInk = 0;
            List<StarSystemInfo> highValue = new List<StarSystemInfo>();
            foreach(StarSystemInfo sys in StarSystems)
            {
                StarSystemData sd = (StarSystemData)sys.BrainData;
                if (sys.OwnedByMe)
                {
                    mySystems++;
                    StarShipInfo ce = ClosestStarShip(sys.X, sys.Y, 20, FindShipFlags.Enemy, ShipKnownFlags.Either);
                    if (ce != null)
                        sd.ClosestEnemy = (int)Distance(sys.X, sys.Y, ce.X, ce.Y);
                    foreach(StarShipInfo ship in MyStarShips)
                        if (Distance(sys.X, sys.Y, ship.X, ship.Y) <= 2)
                            sd.Defenders++;
                }
            }
            foreach(StarShipInfo ship in StarShips)
            {
                if (ship.OwnedByMe)
                {
                    if (ship.ShipType == StarShipType.TechShip)
                        numTS++;
                    else if (ship.ShipType == StarShipType.StarTillery)
                        numST++;
                    else if (ship.ShipType == StarShipType.InkSpot)
                        numInk++;
                }
                else
                {
                    if (ship.KnownWeapons > bestWeaponsSeen)
                        bestWeaponsSeen = ship.KnownWeapons;
                }
            }

            //collect a list of high-value objects that need defending
            foreach(StarSystemInfo sys in StarSystems)
                if (sys.OwnedByMe && sys.Value >= 16)
                    highValue.Add(sys);
    
            //create a list of starsystems that are high value build centers
            List<StarSystemInfo> builders = new List<StarSystemInfo>();
            foreach(StarSystemInfo sys in StarSystems)
                if (sys.OwnedByMe && (!sys.IsCapital || mySystems > 2))
                {
                    StarSystemData sd = (StarSystemData)sys.BrainData;
                    if (sys.Value >= 13 && sd.ClosestEnemy > 15)
                        if (GetTerrain(sys.X, sys.Y) == TerrainType.Clear)
                            builders.Add(sys);
                }

            //make sure not all systems are builders in the beginning
            int maxB = 999;
            if (mySystems < 4)
                maxB = 0;
            else if (mySystems < 7)
                maxB = 1;
            else if (mySystems < 10)
                maxB = 2;
            if (mySystems <= maxB)
            {
                while(builders.Count > maxB)
                    builders.RemoveAt(0);
            }
    
            //build
            foreach(StarSystemInfo sys in StarSystems)
                if (sys.OwnedByMe)
                {
                    StarSystemData sd = (StarSystemData)sys.BrainData;
                    SetAutoShields(sys, false);
                    SetAutoStarShip(sys, false);
                    int defend = 15 - sd.ClosestEnemy;
                    if (defend > 10)
                        defend = 10;
                    if (defend > sys.Value)
                        defend = Random(sys.Value, defend);
                    defend -= sd.Defenders;
                    if (sd.ClosestEnemy < 4 && sys.Shields < 2 && defend < 2)
                        defend = 2;
            
                    if (sys.Shields < defend)
                    {
                        if (sys.Resources > 20)
                            BuildStarShip(sys, StarShipType.EMine, 1, 0, false);
                
                        int i = Random(0, defend);
                        while(i > 0)
                        {
                            if (Random(0, 9) > 6)
                                BuildStarShip(sys, StarShipType.WarShip, 3, 2, false);
                            else
                                BuildStarShip(sys, StarShipType.Drone, 2, 0, false);
                            i--;
                        }
                        BuildShields(sys, defend - sys.Shields);
                    }
                
                    if (sys.IsCapital)
                        if (sd.ClosestEnemy < sd.Defenders * 2)
                        {
                            SetAutoShields(sys, true);
                            SetAutoStarShipType(sys, StarShipType.Drone, 2, 0, false);
                            SetAutoStarShip(sys, true);
                        }
            
                    //scanners
                    if (sys.Value > 10 && sys.Scanners < sys.Value / 4)
                        if (GetTerrain(sys.X, sys.Y) == TerrainType.Clear)
                            BuildScanners(sys, 1);
            
                    //pump out techships
                    if ((Random(0, 9) > 6 || numTS == 0) && !sys.IsCapital)
                    {
                        if (sd.ClosestEnemy > 10)
                        {
                            int ts = sys.Resources / 25;
                            while(ts > 0)
                            {
                                BuildStarShip(sys, StarShipType.TechShip, 3, 0, false);
                                ts--;
                            }
                        }
                    }
            
                    //build up resources
                    if (sd.ClosestEnemy > 10 && numTS < mySystems && Random(0, 99) > 50 && !sys.IsCapital)
                        continue;
            
                    //do we need defenders?
                    double factor = sys.Resources / 25.0 * mySystems;
                    if (highValue.Count > 0 && builders.Contains(sys))
                    {
                        builders.Remove(sys);
                        int engines = (int)(15 * factor * SVGame.RNG.NextDouble());
                        if (engines < 13)
                            engines = 13;
                        if (engines > 20)
                            engines = 20;
                        int value = (int)(factor * SVGame.RNG.NextDouble());
                        if (value < 4)
                            value = 4;
                        if (numST < 1)
                            BuildStarShip(sys, StarShipType.StarTillery, 20, Random(8, 12), false);
                        else
                        {
                            //set task ID
                            if (sd.TaskID == 0 || sys.Resources < sd.LastResources)
                                sd.TaskID = Random(1, 3);
                            if (sd.TaskID == 2)
                                if (numInk > mySystems * 4)
                                    sd.TaskID = 3;
                            sd.LastResources = sys.Resources;
                    
                            //have some fun
                            if (sd.TaskID == 1)
                                BuildStarShip(sys, StarShipType.StarTillery, 20, Random(10, 20), false);
                            else if (sd.TaskID == 2)
                            {
                                int en = (int)(12 * SVGame.RNG.NextDouble() * SVGame.RNG.NextDouble());
                                if (en < 4)
                                    en = 4;
                                for(int n = 0; n < 8; n++)
                                    BuildStarShip(sys, StarShipType.InkSpot, en, 0, sys.Resources > Random(0, 40));
                            }
                            else
                            {
                                int en = (int)(5 * factor * SVGame.RNG.NextDouble());
                                if (en < 8)
                                    en = 8;
                                int val = (int)(5 * factor * SVGame.RNG.NextDouble() * SVGame.RNG.NextDouble());
                                if (val < 4)
                                    val = 4;
                                if (val > 10)
                                    val = 10;
                                for(int n = 0; n < 8; n++)
                                    BuildStarShip(sys, StarShipType.WarShip, en, val, sys.Resources > Random(0, 10));
                            }
                        }
                    }
                    else
                    {
                        //make a list of values we need
                        RandomList<int> valuesNeeded = new RandomList<int>();
                        foreach(StarSystemInfo closeby in StarSystems)
                            if (!closeby.OwnedByMe && closeby.Visible)
                                if (DistanceQuick(sys.X, sys.Y, closeby.X, closeby.Y) < 8)
                                {
                                    int needed = closeby.Shields + 1;
                                    if (needed < 6)
                                        valuesNeeded.Add(needed);
                                }

                        //ensure we build SOMETHING
                        while (valuesNeeded.Count < 2)
                            valuesNeeded.Add(Random(3, 6));
                
                        //pump out warships
                        for(int n = 0; n < 8; n++)
                        {
                            int engines = (int)(5 * factor * SVGame.RNG.NextDouble());
                            if (engines > 20)
                                engines = Random(16, 20);
                            if (engines < 4)
                                engines = 4;
                            int value = (int)(Random(3, 5) * factor * SVGame.RNG.NextDouble());
                            if (value <= 3)
                                value = Random(3, 6);
                            if (!_firstWavePassed)
                            {
                                if (value > 6)
                                    value = Random(4, 7);
                                if (engines > 12)
                                    engines = Random(8, 13);
                            }
                            int rnd = Random(0, 9);
                    
                            //use a needed value first
                            if (valuesNeeded.Count > 0)
                                value = valuesNeeded.RemoveRandomValue;
                            if (value < 3)
                                value = 3;
                
                            if (sd.ClosestEnemy > 16)
                                rnd = 1;
                
                            switch (rnd)
                            {
                                case 0:
                                {
                                    BuildStarShip(sys, StarShipType.Drone, engines, 0, Random(0, 99) > 80);
                                    break;
                                }
                                default:
                                {
                                    if (engines < 6)
                                        engines = Random(5, 8);
                                    BuildStarShip(sys, StarShipType.WarShip, engines, value, false);
                                    break;
                                }
                            }
                        }
                    }
                }

            _firstWavePassed = true;
    
            //clear outdated targets
            foreach(StarShipInfo ship in MyStarShips)
            {
                StarShipData sd = (StarShipData)ship.BrainData;
                if (sd.TargetSystemA != null)
                    if (sd.TargetSystemA.OwnedByMe)
                        sd.TargetSystemA = null;
            }
    
            //command
            StarSystemInfo target;
            StarShipInfo targShip;
            List<StarSystemInfo> defending = new List<StarSystemInfo>();
            foreach(StarShipInfo ship in MyStarShips)
            {
                StarShipData sd = (StarShipData)ship.BrainData;
                switch (ship.ShipType)
                {
                    case StarShipType.WarShip:
                    {
                        //opportunity attacks
                        target = ClosestStarSystem(ship.X, ship.Y, 4, FindSystemFlags.Unfriendly);
                        if (target != null)
                        {
                            sd.TargetSystemA = target;
                            Target(ship, target);
                            continue;
                        }
                
                        if (sd.TargetSystemA == null)
                        {
                            //find closest system that should be a target
                            int dist = Int32.MaxValue;
                            foreach(StarSystemInfo sys in StarSystems)
                            {
                                StarSystemData sysData = (StarSystemData)sys.BrainData;
                                if (!sys.OwnedByMe && sysData.ShieldsRemaining >= 0)
                                {
                                    int d = (int)Distance(ship.X, ship.Y, sys.X, sys.Y);
                                    if (d < dist)
                                    {
                                        dist = d;
                                        target = sys;
                                    }
                                }
                            }
                            if (target == null)
                                target = ClosestStarSystem(ship.X, ship.Y, Int32.MaxValue, FindSystemFlags.Unfriendly);
                    
                            if (target != null)
                            {
                                Target(ship, target);
                                StarSystemData sysData = (StarSystemData)target.BrainData;
                                sysData.ShieldsRemaining -= ship.Value;
                            }
                        }
                
                        if (sd.TargetSystemA != null)
                            AttackAdjacent(ship, !ship.IsAdjacentTo(sd.TargetSystemA));
        
                        break;
                    }
                    case StarShipType.TechShip:
                    {
                        int x = ship.X > MapWidth / 2 ? MapWidth : 0;
                        int y = ship.Y > MapHeight / 2 ? MapHeight : 0;
                        Move(ship, x, y);
                        break;
                    }
                    case StarShipType.InkSpot:
                    {
                        if (sd.TargetCode != null)
                        {
                            //see if target is destroyed/non-visible
                            targShip = FindStarShip(sd.TargetCode);
                            if (targShip == null)
                                sd.TargetCode = null;
                    
                            //find closest unmarked target ship
                            if (sd.TargetCode == null)
                            {
                                int dist = Int32.MaxValue;
                                targShip = null;
                                foreach(StarShipInfo candidate in StarShips)
                                    if (!candidate.OwnedByMe)
                                    {
                                        StarShipData cd = (StarShipData)candidate.BrainData;
                                        if (!cd.Targeted)
                                            if (!candidate.IdentifiedByMe)
                                            {
                                                int d = (int)Distance(ship.X, ship.Y, candidate.X, candidate.Y);
                                                if (d < dist)
                                                {
                                                    dist = d;
                                                    targShip = candidate;
                                                }
                                            }
                                    }
                                if (targShip != null)
                                {
                                    StarShipData targetData = (StarShipData)targShip.BrainData;
                                    targetData.Targeted = true;
                                    sd.TargetCode = targShip.Code;
                                    Target(ship, targShip);
                                }
                            }
                        }
                
                        //no target?
                        if (sd.TargetCode == null)
                        {
                            StarSystemInfo sys = ClosestStarSystem(ship.X, ship.Y, Int32.MaxValue, FindSystemFlags.Unfriendly);
                            Move(ship, sys.X + Random(-3, 3), sys.Y + Random(-3, 3));
                        }
              
                        break;
                    }
                    case StarShipType.StarTillery:
                    {
                        bool repairing = false;
                        if (ship.Health < 60)
                        {
                            PulsarInfo pi = ClosestPulsar(ship.X, ship.Y, Int32.MaxValue);
                            if (pi != null)
                            {
                                repairing = true;
                                Move(ship, pi.X, pi.Y);
                            }
                        }
                
                        if (!repairing)
                        {
                            //defend closest threatened system
                            target = null;
                            int score = 9999;
                            foreach(StarSystemInfo sys in StarSystems)
                                if (sys.OwnedByMe)
                                    if (!defending.Contains(sys))
                                    {
                                        StarSystemData sysData = (StarSystemData)sys.BrainData;
                                        int s = (int)(sysData.ClosestEnemy + Distance(ship.X, ship.Y, sys.X, sys.Y) / 2);
                                        if (s < score)
                                        {
                                            score = s;
                                            target = sys;
                                        }
                                    }
                            if (target != null)
                            {
                                Move(ship, target.X + Random(-2, 2), target.Y + Random(-2, 2));
                                defending.Add(target);
                            }
                            else
                            {
                                StarShipInfo shipTarg = ClosestStarShip(ship.X, ship.Y, Int32.MaxValue, FindShipFlags.Enemy, ShipKnownFlags.Either);
                                if (shipTarg != null)
                                    Move(ship, shipTarg.X + Random(-2, 2), shipTarg.Y + Random(-2, 2));
                                else
                                {
                                    target = ClosestStarSystem(ship.X, ship.Y, Int32.MaxValue, FindSystemFlags.Owned);
                                    Move(ship, target.X + Random(-2, 2), target.Y + Random(-2, 2));
                                }
                            }
                        }
                        break;
                    }
                    case StarShipType.Drone:
                    case StarShipType.EMine:
                        {
                            AttackAdjacent(ship, true);
                            StarShipInfo enemy = ClosestStarShip(ship.X, ship.Y, Int32.MaxValue, FindShipFlags.Enemy, ShipKnownFlags.Either);
                            if (enemy != null)
                                Target(ship, enemy);
                            else
                                Move(ship, ship.X + Random(-2, 2), ship.Y + Random(-2, 2));
                            break;
                        }
                    default:
                        break;
                }
            }

        }

        //description
        public override string Description
        {
            get 
            {
                return "A new conquerer ...";
            }
        }

        //private members
        private bool _firstWavePassed = false;
        private Dictionary<StarSystemInfo, int> _shieldsRemaining = new Dictionary<StarSystemInfo, int>();
        private Dictionary<StarSystemInfo, int> _defenders = new Dictionary<StarSystemInfo, int>();
        private Dictionary<StarSystemInfo, int> _closestEnemy = new Dictionary<StarSystemInfo, int>();
    }

    //StarSystem info object
    internal class StarSystemData
    {
        internal int ShieldsRemaining;
        internal int Defenders;
        internal int ClosestEnemy;
        internal int TaskID;
        internal int LastResources;
    }

    //StarShip info object
    internal class StarShipData
    {
        internal bool Targeted;
        internal StarSystemInfo TargetSystemA;
        internal string TargetCode;
    }
}
